/*
    Copyright © 2010  Ahti Legonkov
    
    This file is part of j+-.
    
    j+- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    j+- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package cx.ath.skyflyer.javapm.visitor;

import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.ConstructorDeclaration;
import japa.parser.ast.body.DestructorDeclaration;
import japa.parser.ast.body.EnumDeclaration;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.ModifierSet;
import japa.parser.ast.body.TypeDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.visitor.GenericVisitorAdapter;
import java.util.LinkedList;
import java.util.List;

/**
 * The visit methods return a Boolean value indicating whether to keep the
 * visited node or strip it. Return value Boolean.TRUE means that the node
 * should be kept, and Boolean.FALSE means that the node should be stripped.
 * This visitor also removes bodies from methods, constructors and destructor.
 * @author Ahti Legonkov
 */
public class PrivateSymbolStripVisitor extends GenericVisitorAdapter<Boolean,Object> {

    /**
     * Strip all private types from compilation unit.
     */
    @Override
    public Boolean visit(CompilationUnit n, Object arg) {

        if (n.getTypes() != null) {
            for (TypeDeclaration typeDeclaration : n.getTypes()) {
                typeDeclaration.accept(this, arg);
            }
        }

        return Boolean.TRUE;
    }

    /**
     * Strip all private fields, methods, constructors, destructor from the
     * class or interface.
     */
    @Override
    public Boolean visit(ClassOrInterfaceDeclaration n, Object arg) {
        if (n.getMembers() == null)
            return Boolean.TRUE;

        List<BodyDeclaration> newMembers = new LinkedList();
        for (BodyDeclaration member : n.getMembers()) {
            Boolean keep = member.accept(this, arg);
            if (keep != null && keep.booleanValue())
                newMembers.add(member);
        }

        n.setMembers(newMembers);
        return Boolean.TRUE;
    }

    /**
     * Indicate whether to keep the constructor or not. Removes body of the
     * constructor if the constructor is to be kept.
     */
    @Override
    public Boolean visit(ConstructorDeclaration n, Object arg) {
        int modifiers = n.getModifiers();
        if (ModifierSet.isPrivate(modifiers))
            return Boolean.FALSE;

        n.setBlock(null);
        return Boolean.TRUE;
    }

    /**
     * Indicate whether to keep the destructor or not. Removes body of the
     * destructor if it is to be kept.
     */
    @Override
    public Boolean visit(DestructorDeclaration n, Object arg) {
        int modifiers = n.getModifiers();
        if (ModifierSet.isPrivate(modifiers))
            return Boolean.FALSE;

        n.setBlock(null);
        return Boolean.TRUE;
    }

    /**
     * Indicate whether to keep the enumeration or not.
     */
    @Override
    public Boolean visit(EnumDeclaration n, Object arg) {
        int modifiers = n.getModifiers();
        if (ModifierSet.isPrivate(modifiers))
            return Boolean.FALSE;

        return Boolean.TRUE;
    }

    /**
     * Indicate whether to keep the fields or not.
     */
    @Override
    public Boolean visit(FieldDeclaration n, Object arg) {
        int modifiers = n.getModifiers();
        if (ModifierSet.isPrivate(modifiers))
            return Boolean.FALSE;

        super.visit(n, arg);
        return Boolean.TRUE;
    }

    /**
     * Indicate whether to keep the method or not.
     */
    @Override
    public Boolean visit(MethodDeclaration n, Object arg) {
        int modifiers = n.getModifiers();
        if (ModifierSet.isPrivate(modifiers))
            return Boolean.FALSE;

        n.setBody(null);
        return Boolean.TRUE;
    }

    /**
     * Visit the variable declatators to remove the initializers if there are
     * any.
     */
    @Override
    public Boolean visit(VariableDeclarationExpr n, Object arg) {
        if (ModifierSet.isPrivate(n.getModifiers()))
            return Boolean.FALSE;

        for (VariableDeclarator v : n.getVars()) {
            v.accept(this, arg);
        }

        return Boolean.TRUE;
    }

    /**
     * Removes initializer from variable declaration.
     */
    @Override
    public Boolean visit(VariableDeclarator n, Object arg) {
        n.setInit(null);
        return Boolean.TRUE;
    }
}
